package com.sunchangming.rpcgen;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.log4j.Logger;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;

import freemarker.template.Template;
import freemarker.template.TemplateException;

/**
 * 
 * @author cm XXX:我覆盖了equals但没有覆盖hashCode!!!
 */
@XStreamAlias("namespace")
public class NameSpace {
	private final static Logger logger = Logger.getLogger(NameSpace.class);

	ArrayList<Enum> enums = new ArrayList<Enum>();

	public static XStream getXStream() {
		final XStream xstream = new XStream(new DomDriver());
		xstream.autodetectAnnotations(true);
		xstream.alias("namespace", NameSpace.class);
		xstream.registerConverter(new BeanConverter());
		xstream.registerConverter(new NameSpaceConverter());
		xstream.registerConverter(new MyEnumConverter());
		return xstream;
	}

	/**
	 * generate code for this namespace
	 * 
	 * @param src
	 *            Top Source Dir
	 * @throws IOException
	 */
	public void generateCode(Path src, String language) throws IOException, TemplateException {
		final Path p = getDir(src);
		if (!java.nio.file.Files.exists(p))
			java.nio.file.Files.createDirectories(p);
		if (!java.nio.file.Files.exists(p)) {
			logger.error("output dir doesn't exists");
			return;
		}
		for (final NameSpace ns : childs) {
			ns.generateCode(src, language);
		}

		for (final Bean bean : beans) {
			bean.generateCode(src, language);
		}
		for (final Enum e : enums) {
			e.generateCode(src, language);
		}

		/*
		 * for (final Message msg : this.messages) { msg.generateCode(src); }
		 */

		if (this.parent == null) {

			final Path outputFileDir = src.resolve("com").resolve("tianshen").resolve("message");
			final Path outputFile = outputFileDir.resolve("MessageDecoder.java");
			if (!java.nio.file.Files.exists(outputFileDir))
				java.nio.file.Files.createDirectories(outputFileDir);
			if (!java.nio.file.Files.exists(outputFileDir)) {
				logger.error("output dir doesn't exists," + outputFileDir.toAbsolutePath().toString());
				return;
			}
			final freemarker.template.Configuration cfg = Main.getFreeMarkerConfig();
			final Template tmpl = cfg.getTemplate("MessageDecoder.tmpl");
			final Map<Object, Object> root = new HashMap<Object, Object>();
			root.put("packagename", "com.tianshen.message");
			root.put("classname", "MessageDecoder");
			root.put("msgs", getAllBeans().values());
			tmpl.process(root, new OutputStreamWriter(Files.newOutputStream(outputFile), "utf-8"));
		}
	}

	ArrayList<NameSpace> childs = new ArrayList<NameSpace>();
	ArrayList<Bean> beans = new ArrayList<Bean>();

	String name;
	/** 最大协议号 */
	int max;

	/** 最小协议号 */
	int min;

	/** 不被持久化 */
	NameSpace parent;

	public Map<Integer, Bean> getAllBeans() {
		final Map<Integer, Bean> msgs = new HashMap<Integer, Bean>();
		for (final Bean b : this.beans) {
			if (min > 0 && b.code > 0 && b.code < min)
				throw new ConfigError("消息" + b.getFullName() + "的消息号应当大于" + min);
			if (max > 0 && b.code > 0 && b.code > max)
				throw new ConfigError("消息" + b.getFullName() + "的消息号应当小于" + max);
			Bean old;
			if (b.code > 0 && (old = msgs.put(b.code, b)) != null)
				throw new ConfigError(old.getFullName() + "和" + b.getFullName() + "具有相同的协议号" + b.code);
		}
		for (final NameSpace n : this.childs) {
			for (final Entry<Integer, Bean> e : n.getAllBeans().entrySet()) {
				if (min > 0 && e.getKey() < min)
					throw new ConfigError("消息" + e.getValue().getFullName() + "的消息号应当小于" + min);
				if (max > 0 && e.getKey() > max)
					throw new ConfigError("消息" + e.getValue().getFullName() + "的消息号应当大于" + max);
				msgs.put(e.getKey(), e.getValue());
			}
		}
		return msgs;
	}

	/**
	 * 搜集所有的自定义类型并把它加入到Types单件中。
	 */
	public void types() {
		for (final Bean b : this.beans) {
			final TypeBean tb = new TypeBean(b, false);
			Types.getInstance().add(tb.getFullName(), tb);
		}

		for (final Enum b : this.enums) {
			final TypeBean tb = new TypeBean(b, true);
			Types.getInstance().add(tb.getFullName(), tb);
		}

		for (final NameSpace n : this.childs) {
			n.types();
		}
	}

	/**
	 * 为每个variable的type根据名字找到对应的实例。
	 */
	public void compile() {
		for (final Bean b : this.beans) {
			b.compile();
		}
		for (final NameSpace n : this.childs) {
			n.compile();
		}
	}

	/**
	 * 获取完全限定名，如com.sunchangming.Req这样的。
	 * 
	 * @return
	 */
	public String getFullName() {
		String ret = name;
		NameSpace ns = this;
		while (ns.parent != null) {
			ns = ns.parent;
			ret = ns.name + "." + ret;
		}
		return ret;
	}

	public java.nio.file.Path getDir(java.nio.file.Path topSourceDir) {
		final ArrayList<NameSpace> list = new ArrayList<NameSpace>();
		NameSpace ns = this;
		list.add(this);
		while (ns.parent != null) {
			ns = ns.parent;
			list.add(ns);
		}
		java.nio.file.Path dir = topSourceDir;
		// 逆序遍历
		for (int i = list.size(); i != 0; --i) {
			final NameSpace ns1 = list.get(i - 1);
			dir = dir.resolve(ns1.name);
		}
		return dir;
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof NameSpace))
			return false;
		final NameSpace other = (NameSpace) obj;
		if (!childs.equals(other.childs))
			return false;
		if (!beans.equals(other.beans))
			return false;
		if (!enums.equals(other.enums))
			return false;
		if (!name.equals(name))
			return false;
		return true;
	}

}
